﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Jayden.Dll.Core.Collections
{
	public class BitArray : IBitArray
	{
		private const int m_ValueSize = 64;

		private ulong[] m_Value;
		private int m_NumberOfBits;
		private int m_NumberOfBitsOn = -1;

		public BitArray(int numberOfBits)
		{
			int length = (numberOfBits / m_ValueSize);
			if ((numberOfBits % m_ValueSize) != 0)
				length++;
			m_Value = new ulong[length];
			m_NumberOfBits = numberOfBits;
		}

		public int NumberOfBits { get { return m_NumberOfBits; } }

		private static int NumberOfSetBits(ulong i)
		{
			i = i - ((i >> 1) & 0x5555555555555555UL);
			i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL);
			return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56);
		}

		public int NumberOfBitsOn
		{
			get
			{
				if (m_NumberOfBitsOn == -1)
				{
					int totalCount = 0;
					for (int c = 0; c < m_Value.Length; c++)
					{
						totalCount += NumberOfSetBits(m_Value[c]);
					}
					m_NumberOfBitsOn = totalCount;
				}
				return m_NumberOfBitsOn;
			}
		}

		public int NumberOfBitsOff { get { return m_NumberOfBits - NumberOfBitsOn; } }

		public bool this[int bitIndex]
		{
			get
			{
				int index = bitIndex / m_ValueSize;
				int shift = bitIndex % m_ValueSize;
				return (m_Value[index] & (1ul << shift)) != 0;
			}
			set
			{
				int index = bitIndex / m_ValueSize;
				int shift = bitIndex % m_ValueSize;
				if (value == ((m_Value[index] & (1ul << shift)) != 0))
					return;
				if (value)
					m_Value[index] |= (1ul << shift);
				else
					m_Value[index] &= ~(1ul << shift);
				m_NumberOfBitsOn = -1;
			}
		}

		public void And(IBitArray value)
		{
			BitArray array = (BitArray)value;
			if (array == null || value.NumberOfBits != NumberOfBits)
				throw new NotImplementedException();
			for (int c = 0; c < m_Value.Length; c++)
				m_Value[c] &= array.m_Value[c];
			m_NumberOfBitsOn = -1;
		}

		public void Or(IBitArray value)
		{
			BitArray array = (BitArray)value;
			if (array == null || value.NumberOfBits != NumberOfBits)
				throw new NotImplementedException();
			for (int c = 0; c < m_Value.Length; c++)
				m_Value[c] |= array.m_Value[c];
			m_NumberOfBitsOn = -1;
		}

		public void Not()
		{
			for (int c = 0; c < m_Value.Length; c++)
				m_Value[c] = ~m_Value[c];
			int shift = m_NumberOfBits % m_ValueSize;
			if (shift != 0)
			{ 
				ulong endMask = 0x00;
				for (int c = 0; c < shift; c++)
					endMask = (endMask << 1) | 1ul;
				m_Value[m_Value.Length - 1] &= endMask;
			}
			m_NumberOfBitsOn = -1;
		}

		public void Xor(IBitArray value)
		{
			BitArray array = (BitArray)value;
			if (array == null || value.NumberOfBits != NumberOfBits)
				throw new NotImplementedException();
			for (int c = 0; c < m_Value.Length; c++)
				m_Value[c] ^= array.m_Value[c];
			m_NumberOfBitsOn = -1;
		}

		public IBitArray Clone()
		{
			throw new NotImplementedException();
		}

		object ICloneable.Clone()
		{
			throw new NotImplementedException();
		}
	}
}
